package game.Player;

import game.Audio.AudioResources;
import game.Configuration;
import game.Gamemode.ChatMessage;
import game.Gamemode.GamemodeManager;
import game.Gamemode.StandardManager;
import game.Gamemode.TeamDeathmatchManager;
import game.Helper;
import game.Map.Map;
import game.Rendering.PointLight;
import game.Scene;
import game.Sprites.*;
import game.Utils.AnimatedDouble;
import game.Utils.GraphicsUtils;
import game.Vector3;
import game.Weapons.Bullet;
import game.Weapons.DroppedWeapon;
import game.Weapons.Weapon_info;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.BlendMode;
import javafx.scene.effect.ColorAdjust;

import javax.sound.sampled.Clip;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javafx.scene.image.Image;
import javafx.scene.paint.Color;

public class Player extends AnimatedSprite implements Damageable {
    public static Player myPlayer;
    protected Controller controller;

    final static double legsAnimTime = 1.0;
    final static double stepSoundSpacing = 1.0;
    Vector3 lastStepPos;

    protected Vector3 tilePos;

    protected String playerName;
    protected SpriteSheet playerSkin;

    protected int playerHealth;
    protected int playerArmor;
    protected Vector3 playerSpeed;
    public double defaultSpeed = 3;
    public double runSpeed = 4;
    private final double playerRadius = 0.5;

    protected Team team;

    public Weapon_info initialWeapon;
    public Weapon_info primary_weapon;
    public Weapon_info secondary_weapon;
    public Weapon_info knife;
    public Weapon_info current_weapon;
    private boolean pickingWeapon;

    public int deathCount = 0;
    public int killCount = 0;

    public int earnedMoney = 0; //Money earned in current round
    public int money = 0;
    public int lastEarnedMoney = 0;
    public long lastEarnedMoneyTime = 0;

    public boolean freeze;
    private boolean reloading;
    private long reloadTime;

    public long timePrev;

    protected AnimatedDouble recoilAnim = new AnimatedDouble(1.0, 0.0, 0.25, true);
    protected AnimatedDouble knifeAnim = new AnimatedDouble(1.0, 0.0, 0.25, true);

    public boolean hasBomb = false;
    public boolean hasDefuseKit = false;
    public Long startedPlanting = null;
    public Long startedDefusing = null;
    protected long plantTime = 2000;
    protected long defusingTime = 6000;

    /**
     * Constructor.
     *
     * @param team     team the player belongs to.
     * @param money    player's starting money.
     * @param position player's starting position.
     * @param rotation player's starting rotation.
     * @param color    for sprite painting...
     */
    //TODO: Color sobra?
    public Player(String name, Team team, int money, Vector3 position, double rotation, Color color) throws Exception {
        super(position, rotation, 1.0, 1.0, color, PlayerSkins.getLegsSheet(), legsAnimTime);

        team.addPlayer(this);

        this.playerName = name;
        this.tilePos = new Vector3((int) position.x, (int) position.y, (int) position.z);
        this.playerSpeed = new Vector3(0.0, 0.0, 0.0);
        this.team = team;
        this.money = money;
        this.lastEarnedMoneyTime = System.currentTimeMillis();
        this.freeze = false;
        this.reloading = false;
        this.pickingWeapon = false;
        this.lastStepPos = position;
        this.playerArmor = 0;

        this.timePrev = System.currentTimeMillis();
        this.secondary_weapon = team.getInitialWeapon();
        this.knife = team.getKinfe();
        this.current_weapon = this.secondary_weapon;

        this.playerSkin = team.getRandomSkin();

        // Add player to player list
        GamemodeManager.getInstance().addPlayer(this);
    }

    public void setController(Controller controller) {
        this.controller = controller;
    }

    public Controller getController() {
        return this.controller;
    }

    /**
     * If possible, moves the player to the given position.
     *
     * @param newPos new desired position for the player.
     */
    public void movePlayer(Vector3 newPos) {
        Vector3 localMapPos = Helper.mainScene.getMap().globalToLocal(newPos);

        Ellipse2D.Double playerCircle = new Ellipse2D.Double(localMapPos.x - playerRadius / 2, localMapPos.y - playerRadius / 2, playerRadius, playerRadius);

        boolean collides = false;
        for (int x = (int) localMapPos.x - 1; x <= (int) localMapPos.x + 1; x++) {
            for (int y = (int) localMapPos.y - 1; y <= (int) localMapPos.y + 1; y++) {
                Rectangle2D.Double tileRectangle = new Rectangle2D.Double(x, y, 1.0, 1.0);
                if (!Helper.mainScene.getMap().isWalkable(x, y) && playerCircle.intersects(tileRectangle)) {
                    collides = true;
                    break;
                }

            }
        }

        if (!collides) {
            this.setPosition(newPos);
        }
    }

    /**
     * Draws the player.
     *
     * @param g2D
     * @param screenPos
     * @param squareRes
     */
    @Override
    public void draw(GraphicsContext g2D, Vector3 screenPos, double squareRes) {
        double theta = this.getRotation();
        double legsTheta = Math.atan2(this.playerSpeed.y, this.playerSpeed.x);
        if (this.isAlive()) {

            //  Player shadow
            //g2D.drawImage(SpriteResources.getShadowImage(), (int) screenPos.x - (int) squareRes / 2, (int) screenPos.y - (int) squareRes / 2,
            //        (int) squareRes, (int) squareRes);
            GraphicsUtils.drawSpriteShadow(g2D, screenPos, 1.0, 1.0, squareRes);

            // Player legs
            super.draw(g2D, screenPos, squareRes);
        } else
            return;


        //g2D.rotate(theta, (int) screenPos.x, (int) screenPos.y);// PI to Fix player rotation

        // Recoil offset
        double recoilAmmount = recoilAnim.getValue();
        if (current_weapon == knife) {
            recoilAmmount = 0.0;
        }
        double recoilMeters = recoilAmmount * 0.1;
        double recoilPixels = recoilMeters * squareRes;

        // Knife rotation
        double knifeRotation = Math.PI / 3 * knifeAnim.getValue();

        //Draw skin
        int skinID = 4;
        if (Configuration.isLeftHanded())
            skinID = 5;
        if (!this.current_weapon.isPrimary() || isReloading())
            skinID = 3;

        GraphicsUtils.drawRotatedImage(g2D, this.playerSkin.get(skinID), theta + knifeRotation + Math.PI / 2,
                (screenPos.x - squareRes / 2) - (Math.cos(theta + knifeRotation) * recoilPixels),
                (screenPos.y - squareRes / 2) - (Math.sin(theta + knifeRotation) * recoilPixels),
                squareRes, squareRes);

        //Draw weapon
        if (this.current_weapon != null && this.current_weapon.getPlayerImage() != null) {

            GraphicsUtils.drawRotatedImage(g2D, this.current_weapon.getPlayerImage(), theta + knifeRotation + Math.PI / 2,
                    (screenPos.x - squareRes / 2) + (Math.cos(theta + knifeRotation) * (squareRes / 2 - recoilPixels)),
                    (screenPos.y - squareRes / 2) + (Math.sin(theta + knifeRotation) * (squareRes / 2 - recoilPixels)),
                    squareRes, squareRes);

        }

        // Draw bomb
        if(this.hasBomb()){
            Image bombImg = SpriteResources.getBombEquipmentImage();
            GraphicsUtils.drawRotatedImage(g2D, bombImg, theta + knifeRotation + Math.PI / 2,
                    (screenPos.x - squareRes / 2) - (Math.cos(theta + knifeRotation) * recoilPixels),
                    (screenPos.y - squareRes / 2) - (Math.sin(theta + knifeRotation) * recoilPixels),
                    squareRes, squareRes);
        }

        // Draw knife slash
        if (knifeAnim.getValue() > 0.0) {
            //g2D.setGlobalAlpha(knifeAnim.getValue());
            ColorAdjust colorAdjust = new ColorAdjust();
            colorAdjust.setBrightness(knifeAnim.getValue() * 2.0 - 1.0);
            g2D.setEffect(colorAdjust);
            g2D.setGlobalBlendMode(BlendMode.ADD);
            GraphicsUtils.drawRotatedImage(g2D, SpriteResources.getKnifeSlash(), theta + Math.PI / 2,
                    (int) screenPos.x - (int) squareRes / 2 - (int) (Math.cos(theta) * (recoilPixels - squareRes / 4)),
                    (int) screenPos.y - (int) squareRes / 2 - (int) (Math.sin(theta) * (recoilPixels - squareRes / 4)),
                    (int) squareRes, (int) squareRes);
            g2D.setGlobalBlendMode(BlendMode.SRC_OVER);
            g2D.setEffect(null);
        }
    }

    /**
     * Returns the biggest rectangle that fits inside player's circle.
     */
    @Override
    public Rectangle2D.Double getBound() {
        return new Rectangle2D.Double(this.position.x - 0.45, this.position.y - 0.45, playerRadius, playerRadius);
    }

    /**
     * Deals damage to the player.
     *
     * @param originalDamage ammount of damage dealt to the player.
     */
    @Override
    public int doDamage(Player inflictor, Weapon_info wpn, int originalDamage) {
        if (this == myPlayer && Scene.getInstance() != null && Scene.getInstance().getCamera() != null
                && Scene.getInstance().getCamera().isStoreOpen())
            Scene.getInstance().getCamera().setStoreOpen(false);

        if (!this.isAlive() || !inflictor.isAlive()) {
            return 0;
        }

        //Calculo obtenido de: http://wiki-en.unrealsoftware.de/index.php/Kevlar_(Counter-Strike_2D)
        int realDamage;
        if (playerArmor > 0) {
            int remainingDmg = originalDamage - playerArmor;
            if (remainingDmg < 0) remainingDmg = 0;
            int blockedDmg = originalDamage - remainingDmg;
            playerArmor -= blockedDmg;
            realDamage = (int) (remainingDmg + blockedDmg * 0.7);
        } else realDamage = originalDamage;
        //System.out.println(this.getName() + " received damage " + originalDamage + " from " + inflictor.getName());
        if (this.playerHealth - realDamage <= 0) {
            // Player is killed
            int doneDamage = this.playerHealth;
            this.playerHealth = 0;
            this.deathCount += 1;
            this.reloading = false; //Stop reloading
            this.pickingWeapon = false;
            this.startedDefusing = null;
            this.startedPlanting = null;

            this.setHasDefuseKit(false);
            //TODO: Drop bomb
            if(this.hasBomb) {
                try {
                    ((StandardManager)GamemodeManager.getInstance()).dropBomb(this);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            if (Helper.myCamera != null) {
                Helper.myCamera.play3DSound(AudioResources.getDieSound(), this.getPosition());
            }

            try {
                if (Configuration.isBlood())
                    new BloodStain(position, rotation);
            } catch (Exception e) {
                e.printStackTrace();
            }

            // Si lleva arma primaria dropearla
            if (this.primary_weapon != null) {
                try {
                    new DroppedWeapon(primary_weapon, 1, this.position, this.rotation, Color.RED);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            GamemodeManager.getInstance().playerKilled(this, inflictor, wpn);

            return doneDamage;
        } else {
            // Player still alive
            this.playerHealth -= realDamage;

            if (Helper.myCamera != null) {
                Helper.myCamera.play3DSound(AudioResources.getHitSound(), this.getPosition());
            }
            this.controller.notifyDamageDone(inflictor);
            return realDamage;
        }

    }

    public boolean isAlive() {
        return this.playerHealth > 0;
    }

    public void tryToReload() {
        if (this.freeze || this.reloading) {
            return;
        } else {
            int ammo_diff = this.current_weapon.ammo_charger_value - this.current_weapon.getAmmo_charger();
            if (ammo_diff > 0 && this.current_weapon.getAmmo_total() > 0) {
                this.reloading = true;
                this.reloadTime = System.currentTimeMillis() + (int) (this.current_weapon.getReloadTime() * 1000);
            }
        }
    }

    public void reload() {
        if (this.freeze) {
            return;
        }
        int ammo_diff = this.current_weapon.ammo_charger_value - this.current_weapon.getAmmo_charger();
        if (ammo_diff > 0 && this.current_weapon.getAmmo_total() > 0) {
            int cargador = this.current_weapon.getAmmo_charger();
            int total = this.current_weapon.getAmmo_total();
            if (ammo_diff < total) {
                this.current_weapon.setAmmo_charger(this.current_weapon.ammo_charger_value);
                this.current_weapon.reload(ammo_diff);
            } else {
                this.current_weapon.setAmmo_charger(cargador + total);
                this.current_weapon.setAmmo_total(0);
            }
        }
    }

    public int check_ammo() {
        if (current_weapon == knife)
            return 1;

        int aux = 0;
        if (current_weapon.getAmmo_total() <= 0 && current_weapon.getAmmo_charger() <= 0) {
            aux = 1;    // No le queda mas municion total, pero si del cargador (5|0)
        }
        if (current_weapon.getAmmo_charger() <= 0) {
            aux = 2;    // Necesita recargar    (0|5)
        }
        return aux;
    }

    public void shoot() {
        if (this.freeze || this.reloading) {
            return;
        }
        //checkear ammo
        boolean canShoot = false;
        switch (check_ammo()) {
            case 0:
                canShoot = true;
                break;
            case 1:
                canShoot = true;
                break;
            case 2:
                canShoot = false;
                this.tryToReload();
                break;
            default:
                break;
        }

        int fireDelay = current_weapon.getFiredelay();

        //limitar cadencia
        long timeNow = System.currentTimeMillis();
        if (canShoot && timeNow - timePrev > 50 * fireDelay) {
            // KNIFE WEAPON
            if (current_weapon == knife) {
                doKnifeDamage();
                knifeAnim.reset();
            }
            // GUN WEAPON
            else {
                try {
                    double randRot = 0;
                    Bullet b = new Bullet(this.getPosition(), this.getRotation() + randRot,
                            Color.YELLOW, this, current_weapon.getDamage(), 200, current_weapon.getRange() / 10.0);

                    // Add bullet LIGHT effect
                    PointLight light = new PointLight(this.getGunPointPosition(), 1.0, Color.YELLOW);
                    light.destroy(0.05);

                    current_weapon.shoot();  // ammo_charger = ammo_charger - 1
                    if (Configuration.isRecoilAnimation())
                        recoilAnim.reset();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            timePrev = timeNow; // Actualizar tiempo

            // reproducir sonido de disparo
            Clip shootingAudio = current_weapon.getShotAudio();
            if (shootingAudio != null) {
                Helper.mainScene.getCamera().play3DSound(shootingAudio, position);
            }
        }

        //If tactical vision is activated make sure bots "hear" shots
        if (Configuration.getTacticVision() && this.current_weapon != knife) {
            List<Player> enemies = new ArrayList<>();
            GamemodeManager gm = GamemodeManager.getInstance();
            if (gm instanceof StandardManager && this.team == ((StandardManager) gm).getCounterTeam()) {
                enemies = ((StandardManager) gm).getTerroristTeam().getPlayers();
            } else if (gm instanceof StandardManager && this.team == ((StandardManager) gm).getTerroristTeam()) {
                enemies = ((StandardManager) gm).getCounterTeam().getPlayers();
            } else if (gm instanceof TeamDeathmatchManager && this.team == ((TeamDeathmatchManager) gm).getCounterTeam()) {
                enemies = ((TeamDeathmatchManager) gm).getTerroristTeam().getPlayers();
            } else if (gm instanceof TeamDeathmatchManager && this.team == ((TeamDeathmatchManager) gm).getTerroristTeam()) {
                enemies = ((TeamDeathmatchManager) gm).getCounterTeam().getPlayers();
            }
            for (Player p : enemies) {
                if (p.getController() instanceof BotController) {
                    //TODO: Revisar "alcance"
                    if (Vector3.distance2d(p.getPosition(), this.position) <= 6.0 * Helper.mainScene.getMap().getTileSize()) {
                        ((BotController) p.getController()).notifyShotFired(this);
                    }
                }
            }
        }
    }

    public int getAmmoCharger() {
        return this.current_weapon.getAmmo_charger();
    }

    public int getAmmoTotal() {
        return this.current_weapon.getAmmo_total();
    }

    public void tryToAtlernate() {
        if (current_weapon == primary_weapon) {
            changeToSecondary();
        } else if (current_weapon == secondary_weapon) {
            changeToKnife();
        } else if (current_weapon == knife) {
            if (primary_weapon == null) {
                changeToSecondary();
            } else {
                changeToPrimary();
            }
        } else {
            changeToPrimary();
        }
    }

    public void tryPickUpWeapon() {
        if (this.freeze || this.reloading) {
            return;
        } else {
            pickingWeapon = true;
        }
    }

    public Weapon_info interchangeWeapon(Weapon_info newWeapon) {
        if (primary_weapon == null) {
            this.primary_weapon = newWeapon;
            changeToPrimary();
            return null;
        }
        Weapon_info weaponToDrop = primary_weapon;
        this.primary_weapon = newWeapon;
        changeToPrimary();
        return weaponToDrop;
    }

    protected void doKnifeDamage() {
        double knifeDistance = 0.8;
        double knifeAngle = 2.0; //Radians (115 degrees)

        List<Player> playerList = GamemodeManager.getInstance().getPlayerList();
        for (int i = 0; i < playerList.size(); i++) {
            Player pl = playerList.get(i);
            if (pl == this)
                continue;

            double dist = Vector3.distance(pl.getPosition(), new Vector3(this.getX(), this.getY(), pl.getZ()));
            Vector3 direction = pl.getPosition().sub(this.getPosition());
            double angle = Math.atan2(direction.y, direction.x) - this.rotation;
            if (dist < knifeDistance) {
                boolean hit = GamemodeManager.getInstance().knifeHit(this, knife, pl);
                if (hit) {
                    // Create knife blood
                    if (Configuration.isBlood()) {
                        try {
                            for (int j = 0; j < 3; j++) {
                                double randRot = (Math.random() - 0.5) * 0.15;
                                new BloodSprite(pl.getPosition(), -pl.rotation + randRot);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    public void changeToPrimary() {
        if (current_weapon == primary_weapon) {
            return;
        }
        reloading = false;
        pickingWeapon = false;
        if (primary_weapon != null) {
            current_weapon = primary_weapon;
            //Helper.myCamera.play3DSound(AudioResources.getWeaponChangeSound(), this.getPosition());
        }
    }

    public void changeToSecondary() {
        if (current_weapon == secondary_weapon) {
            return;
        }
        reloading = false;
        pickingWeapon = false;
        if (secondary_weapon != null) {
            current_weapon = secondary_weapon;
            //Helper.myCamera.play3DSound(AudioResources.getWeaponChangeSound(), this.getPosition());
        }
    }

    public void changeToKnife() {
        if (current_weapon == knife) {
            return;
        }
        reloading = false;
        pickingWeapon = false;
        if (knife != null) {
            current_weapon = knife;
            //Helper.myCamera.play3DSound(AudioResources.getWeaponChangeSound(), this.getPosition());
        }
    }

    public boolean getFreeze() {
        return this.freeze;
    }

    public void setFrozen(boolean b) {
        this.freeze = b;
        this.reloading = false;
        this.pickingWeapon = false;
    }

    public Vector3 getGunPointPosition() {
        boolean primary = false;
        if (current_weapon != null && current_weapon.isPrimary())
            primary = true;

        Vector3 dirPos = new Vector3(Math.cos(rotation), Math.sin(rotation), 0.0);
        if (primary)
            return this.position.add(dirPos);
        else
            return this.position.add(dirPos.multiply(0.5));
    }

    /**
     * Returns player's current health.
     */
    @Override
    public int getHealth() {
        return this.playerHealth;
    }

    public int setHealth(int health) {
        this.playerHealth = health;
        return health;
    }

    /**
     * Returns the name of the player.
     *
     * @return The name of the player.
     */
    public String getName() {
        return this.playerName;
    }

    /**
     * Sets the name of the player.
     */
    public void setName(String name) {
        this.playerName = name;
    }

    /**
     * Returns player's current team.
     */
    public Team getTeam() {
        return this.team;
    }

    /**
     * Changes a player team
     *
     * @param team
     */
    public void switchTeam(Team team) {
        this.team = team;

        this.playerSkin = team.getRandomSkin();
    }

    /**
     * Adds kills to the player kill count.
     *
     * @param nKills number of kills to be added.
     */
    public void addKill(int nKills) {
        this.killCount += nKills;
    }

    /**
     * Adds deaths to the player death count.
     *
     * @param nDeatsh number of deaths to be added.
     */
    public void addDeath(int nDeatsh) {
        this.deathCount += nDeatsh;
    }


    /**
     * Adds money to the player budget.
     *
     * @param money ammount to be added.
     */
    public void addMoney(int money) {
        this.money += money;
    }

    public void earnMoney(int quantity) {
        this.earnedMoney += quantity;
    }

    public void giveEarnedMoney() {
        this.money += this.earnedMoney;
        this.lastEarnedMoney = this.earnedMoney;
        this.lastEarnedMoneyTime = System.currentTimeMillis();
        this.earnedMoney = 0;

        if (this == myPlayer && Helper.myCamera != null) {
            Helper.myCamera.moneyAnim.reset();
        }
    }

    /**
     * Sets money to the player budget.
     *
     * @param money ammount to be set.
     */
    public void setMoney(int money) {
        this.money = money;
    }

    /**
     * Removes money to the player budget.
     * Returns false if the player doesn't have enough money.
     *
     * @param money ammount to be removed.
     */
    //TODO: Ahora mismo false=no se puede retirar esa cantidad
    public boolean removeMoney(int money) {
        if (money > this.money) return false;
        else {
            this.money -= money;
            return true;
        }
    }

    public boolean isReloading() {
        return this.reloading;
    }

    public boolean isPicking() {
        return this.pickingWeapon;
    }

    /**
     * In seconds
     *
     * @return
     */
    public double getTimeUntilReload() {
        return (this.reloadTime - System.currentTimeMillis()) / 1000.0;
    }

    @Override
    public void destroy() {
        super.destroy();
        if (Player.myPlayer == this) {
            Player.myPlayer = null;
        }
    }

    @Override
    public void update(double deltaTime) {
        if (this.freeze || !this.isAlive()) {
            this.reloading = false; //Stop reloading
            this.pickingWeapon = false; //Stop picking
            super.resetAnimation();//Stop legs
            return;
        }

        // Check reload
        if (reloading && this.reloadTime < System.currentTimeMillis()) {
            this.reload();
            this.reloading = false;
        }

        // Save player current position
        Vector3 oldPos = this.getPosition();

        // Set speed depending on weapon
        if (this.current_weapon != null) {
            runSpeed = defaultSpeed + current_weapon.getWeight() * 1.5;
        } else {
            runSpeed = defaultSpeed;
        }

        // Move player in X axis
        Vector3 newPos = this.getPosition().add(new Vector3(this.playerSpeed.x, 0.0, 0.0).multiply(deltaTime));
        this.movePlayer(newPos);

        // Move player Y axis
        newPos = this.getPosition().add(new Vector3(0.0, this.playerSpeed.y, 0.0).multiply(deltaTime));
        this.movePlayer(newPos);

        // Animate legs
        double realPlayerSpeed = Vector3.distance(this.getPosition(), oldPos) / deltaTime;
        if (realPlayerSpeed <= 0.01) {
            super.resetAnimation();
        }
        super.setAnimationTime(legsAnimTime * runSpeed / realPlayerSpeed);
        super.update(deltaTime);

        // Play step sounds
        if (Vector3.distance(position, lastStepPos) > stepSoundSpacing) {
            lastStepPos = this.position;
            if (Helper.myCamera != null) {
                Helper.myCamera.play3DSound(AudioResources.getStepSound(), this.getPosition());
            }
        }

        Vector3 newTilePos = new Vector3((int) this.getPosition().x, (int) this.getPosition().y, (int) this.getPosition().z);
        if (!Vector3.equal(newTilePos, this.tilePos)) {
            this.tilePos = newTilePos;
            onPlayerTileChange(deltaTime);
        }

    }

    public void resetWeaponsAmmo() {
        if (primary_weapon != null) {
            this.primary_weapon.getDefault();
        }
        if (secondary_weapon != null) {
            this.secondary_weapon.getDefault();
        }
        if (primary_weapon == null && secondary_weapon != null) {
            this.current_weapon = secondary_weapon;
        }
    }


    /**
     * Called whenever the player changes tile.
     */
    public void onPlayerTileChange(double deltaTime) {

    }

    public int getMoney() {
        return this.money;
    }

    public boolean isInBuyingZone() {
        Ellipse2D.Double playerCircle = new Ellipse2D.Double(this.position.x - playerRadius / 2, this.position.y - playerRadius / 2, playerRadius, playerRadius);

        Map map = Helper.mainScene.getMap();
        List<Vector3> tSpawn = map.gettSpawn();
        double xMin = Double.MAX_VALUE, xMax = Double.MIN_VALUE, yMin = Double.MAX_VALUE, yMax = Double.MIN_VALUE;
        for (Vector3 v : tSpawn) {
            if (v.x < xMin) xMin = v.x;
            if (v.x > xMax) xMax = v.x;
            if (v.y < yMin) yMin = v.y;
            if (v.y > yMax) yMax = v.y;
        }
        Rectangle2D.Double buyingRectangle = new Rectangle2D.Double(xMin, yMin, (Helper.mainScene.getMap().getTileSize() + xMax - xMin) * 3, (Helper.mainScene.getMap().getTileSize() + yMax - yMin) * 3);
        if (playerCircle.intersects(buyingRectangle)) return true; //TODO: Cambiar por buyingRectangle.contains... ?
        List<Vector3> ctSpawn = map.getctSpawn();
        xMin = Double.MAX_VALUE;
        xMax = Double.MIN_VALUE;
        yMin = Double.MAX_VALUE;
        yMax = Double.MIN_VALUE;
        for (Vector3 v : ctSpawn) {
            if (v.x < xMin) xMin = v.x;
            if (v.x > xMax) xMax = v.x;
            if (v.y < yMin) yMin = v.y;
            if (v.y > yMax) yMax = v.y;
        }
        buyingRectangle = new Rectangle2D.Double(xMin, yMin, (Helper.mainScene.getMap().getTileSize() + xMax - xMin) * 3, (Helper.mainScene.getMap().getTileSize() + yMax - yMin) * 3);
        if (playerCircle.intersects(buyingRectangle)) return true; //TODO: Cambiar por buyingRectangle.contains... ?
        return false;
    }

    public boolean isInBuyingTime() {
        double shopCloseTime = TeamDeathmatchManager.getInstance().getShopCloseTime();
        return ((double) System.currentTimeMillis() / 1000.0) <= shopCloseTime;
    }

    public boolean tryToBuy(Weapon_info w) {
        boolean result = false;
        if (this.isInBuyingZone() && this.isInBuyingTime()) {
            try {
                //Regular weapon
                if (w.getType() != 6) {
                    if (w.getPrice() <= this.money &&
                            !(this.primary_weapon != null && this.primary_weapon.getName().equals(w.getName())) &&
                            !(this.secondary_weapon != null && this.secondary_weapon.getName().equals(w.getName()))) {
                        this.money -= w.getPrice();
                        if (w.isPrimary()) {
                            this.primary_weapon = (Weapon_info) w.clone();
                            current_weapon = this.primary_weapon;
                        } else {
                            this.secondary_weapon = (Weapon_info) w.clone();
                            current_weapon = this.secondary_weapon;
                        }
                        result = true;
                    } else {
                        if (this == myPlayer && Helper.myCamera != null) {
                            Helper.myCamera.setBuyError();
                        }
                    }
                }
                //Equipment
                else {
                    if(w.getName().equals("defusekit")){
                        if (w.getPrice() <= this.money && !this.hasDefuseKit) {
                            this.setHasDefuseKit(true);
                            this.money -= w.getPrice();
                            result = true;
                        }
                    }
                    //Kevlar
                    else {
                        int maxArmor = w.getDamage();
                        if (maxArmor > playerArmor && money >= (maxArmor - playerArmor) * 10) {
                            money -= (maxArmor - playerArmor) * 10;
                            playerArmor += (maxArmor - playerArmor);
                            result = true;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public Weapon_info getPrimary_weapon() {
        return this.primary_weapon;
    }

    public List<Player> viewableScreenPlayers() {
        List<Player> result = new ArrayList<Player>();
        // Player view rectangle
        double ratio = (double) Helper.screenWidth() / (double) Helper.screenHeight();
        if (Scene.getInstance() != null && Scene.getInstance().getCamera() != null) {
            double recHeight = Scene.getInstance().getCamera().getSize();
            double recWidth = ratio * recHeight;

            Rectangle2D.Double viewRect = new Rectangle2D.Double(this.getX() - recWidth / 2,
                    this.getY() - recHeight / 2, recWidth, recHeight);

            List<Player> playerList = GamemodeManager.getInstance().getPlayerList();
            for (Player pl : playerList) {
                if (!pl.isAlive())
                    continue;
                if (viewRect.intersects(pl.getBound())) {
                    result.add(pl);
                }
            }
        }
        return result;
    }

    public List<Player> tacticViewedPlayers() {
        List<Player> result = new ArrayList<Player>();
        // Player view rectangle
        double ratio = (double) Helper.screenWidth() / (double) Helper.screenHeight();
        if (Scene.getInstance() != null && Scene.getInstance().getMap() != null && Scene.getInstance().getCamera() != null) {
            double recHeight = Scene.getInstance().getCamera().getSize();
            double recWidth = ratio * recHeight;
            Rectangle2D.Double viewRect = new Rectangle2D.Double(this.getX() - recWidth / 2,
                    this.getY() - recHeight / 2, recWidth, recHeight);

            List<Player> playerList = GamemodeManager.getInstance().getPlayerList();
            for (Player pl : playerList) {
                if (!pl.isAlive())
                    continue;
                if (viewRect.intersects(pl.getBound()) &&
                        Scene.getInstance().getMap().is_viewed_from(this.position, pl.getPosition(),
                                this.rotation, Math.toRadians(120))) {
                    result.add(pl);
                }
            }
        }
        return result;
    }

    public List<Player> viewablePlayers() {
        if (!Configuration.getTacticVision()) {
            return viewableScreenPlayers();
        } else {
            return tacticViewedPlayers();
        }
    }

    public List<Player> viewableEnemies() {
        List<Player> result = new ArrayList<Player>();

        List<Player> viewable = this.viewablePlayers();
        for (Player pl : viewable) {
            if (pl.getTeam() != this.getTeam()) {
                result.add(pl);
            }
        }

        return result;
    }

    public Player closestViewableEnemy() {
        Player pl = null;
        List<Player> enemies = viewableEnemies();
        double minDist = 0.0;
        for (int i = 0; i < enemies.size(); i++) {
            Player e = enemies.get(i);
            Vector3 thisPos = this.getPosition();
            thisPos.z = e.getPosition().z;
            double dist = Vector3.distance(e.getPosition(), thisPos);
            if (dist < minDist || pl == null) {
                pl = e;
                minDist = dist;
            }
        }

        return pl;
    }

    public int getDeathCount() {
        return deathCount;
    }

    public int getKillCount() {
        return killCount;
    }

    public void sendTeamMessage(String msg) {
        ChatMessage message = new ChatMessage(this.getName(), this.getTeam().getTeamColor(), msg, false);
        GamemodeManager.getInstance().addChatMessage(message);
    }

    public void sendMessage(String msg) {
        ChatMessage message = new ChatMessage(this.getName(), this.getTeam().getTeamColor(), msg, true);
        GamemodeManager.getInstance().addChatMessage(message);
    }

    public int getPlayerArmor() {
        return playerArmor;
    }

    public void setPlayerArmor(int playerArmor) {
        this.playerArmor = playerArmor;
    }

    public boolean isInBombZone() {
        Ellipse2D.Double playerCircle = new Ellipse2D.Double(this.position.x - playerRadius / 2, this.position.y - playerRadius / 2, playerRadius, playerRadius);

        Map map = Helper.mainScene.getMap();
        List<Map.BombPoint> bombSpots = map.getBombPoints();
        for (Map.BombPoint bp : bombSpots) {
            for (Vector3 v : bp.getPoints()) {
                Rectangle2D.Double buyingRectangle = new Rectangle2D.Double(v.x - map.getTileSize(), v.y - map.getTileSize(), map.getTileSize() * 3, map.getTileSize() * 3);
                if (playerCircle.intersects(buyingRectangle)) return true;
            }
        }
        return false;
    }

    public boolean hasBomb() {
        return hasBomb;
    }

    public void setHasBomb(boolean hasBomb) {
        this.hasBomb = hasBomb;
    }

    public boolean hasDefuseKit() {
        return hasDefuseKit;
    }

    public void setHasDefuseKit(boolean hasDefuseKit) {
        this.hasDefuseKit = hasDefuseKit;
    }

    public void tryToPlantBomb() {
        if (GamemodeManager.getInstance() instanceof StandardManager && hasBomb && isInBombZone()) {
            this.playerSpeed = new Vector3(0,0,0);
            if (startedPlanting == null) {
                startedPlanting = System.currentTimeMillis();
            } else {
                //Plant if player has been planting for 2 secs
                if (System.currentTimeMillis() - startedPlanting >= plantTime) plantBomb();
            }
        }
    }

    public Double plantingProgress(){
        if(startedPlanting != null){
            double p = startedPlanting+plantTime - System.currentTimeMillis();
            p = 1.0 - p/plantTime;
            return new Double(p);
        }else{
            return null;
        }
    }

    public Double defusingProgress(){
        if(startedDefusing != null){
            double p = startedDefusing+defusingTime - System.currentTimeMillis();
            p = 1.0 - p/defusingTime;
            return new Double(p);
        }else{
            return null;
        }
    }

    public void stopPlanting() {
        startedPlanting = null;
    }

    //Method to finish planting the bomb
    public void plantBomb() {
        GamemodeManager gm = GamemodeManager.getInstance();
        startedPlanting = null;
        if (gm instanceof StandardManager && hasBomb && isInBombZone()) {
            hasBomb = false;
            ((StandardManager) gm).plantBomb(this);
        }
    }

    public boolean isPlanting() {
        return hasBomb && startedPlanting != null;
    }


    public void tryToDefuseBomb() {
        if (GamemodeManager.getInstance() instanceof StandardManager && ((StandardManager) GamemodeManager.getInstance()).getBombPosition() != null) {
            if (!this.team.isAnybodyElseDefusing(this) && isInDefusingRange()) {
                if (startedDefusing == null) {
                    startedDefusing = System.currentTimeMillis();
                } else {
                    //TODO: Parametrizar
                    //Defuse if player has been defusing for 10 secs
                    if (hasDefuseKit()) defusingTime /= 2;
                    if (System.currentTimeMillis() - startedDefusing >= defusingTime) defuseBomb();
                }
            }
        }
    }

    public void stopDefusing() {
        startedDefusing = null;
    }

    //Method to finish planting the bomb
    public void defuseBomb() {
        GamemodeManager gm = GamemodeManager.getInstance();
        startedDefusing = null;
        if (gm instanceof StandardManager && ((StandardManager) gm).getBombPosition() != null) {
            if (isInDefusingRange()) ((StandardManager) gm).defuseBomb();
        }
    }

    public boolean isDefusing() {
        return startedDefusing != null;
    }

    public boolean isInDefusingRange() {
        GamemodeManager gm = GamemodeManager.getInstance();
        if (gm instanceof StandardManager && ((StandardManager) gm).getBombPosition() != null) {
            Vector3 bombPos = ((StandardManager) GamemodeManager.getInstance()).getBombPosition();
            Vector3 dif = new Vector3(position.x - bombPos.x, position.y - bombPos.y, 0);
            return dif.mod() <= Helper.mainScene.getMap().getTileSize();
        }
        return false;
    }

    public long getShortestRemainingTime() {
        long plantingRem, defusingRem;
        if(startedPlanting!=null) plantingRem = 2000 - (System.currentTimeMillis() - startedPlanting);
        else plantingRem = Long.MAX_VALUE;
        if(startedDefusing != null) {
            int defusingTime = 6000;
            if (hasDefuseKit()) defusingTime /= 2;
            defusingRem = defusingTime - (System.currentTimeMillis() - startedDefusing);
        }
        else defusingRem = Long.MAX_VALUE;
        if (plantingRem < defusingRem) return plantingRem;
        else return defusingRem;
    }

}
